package ltd.hxya.novel.book.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import ltd.hxya.novel.book.client.UserClient;
import ltd.hxya.novel.book.entity.*;
import ltd.hxya.novel.book.dto.*;
import ltd.hxya.novel.book.feign.CrawlFeignService;
import ltd.hxya.novel.book.feign.SearchFeignService;
import ltd.hxya.novel.book.mapper.BookMapper;
import ltd.hxya.novel.book.service.*;
import ltd.hxya.novel.book.utils.constant.DatabaseConsts;
import ltd.hxya.novel.book.vo.*;
import ltd.hxya.novel.common.anno.OperLogAnno;
import ltd.hxya.novel.common.bean.ModuleEnum;
import ltd.hxya.novel.common.bean.OperEnum;
import ltd.hxya.novel.common.bean.Result;
import ltd.hxya.novel.common.bean.ResultEnum;
import ltd.hxya.novel.common.constant.rabbit.RabbitConstant;
import ltd.hxya.novel.common.constant.redis.RedisConstant;
import ltd.hxya.novel.common.exception.NovelGlobalException;
import ltd.hxya.novel.common.to.book.BookChapterTo;
import ltd.hxya.novel.common.to.book.BookTo;
import ltd.hxya.novel.common.utils.*;
import ltd.hxya.novel.common.vo.PageParam;
import ltd.hxya.novel.common.vo.SearchParam;
import ltd.hxya.novel.entity.rowdata.DataPart;
import org.jetbrains.annotations.NotNull;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 小说表 服务实现类
 * </p>
 *
 * @author hxya
 * @since 2022-09-13
 */
@Service
@Slf4j
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private BookMapper bookMapper;

    @Autowired
    private IBookRankService bookRankService;

    @Autowired
    private SearchFeignService searchFeignService;

    @Autowired
    private CrawlFeignService crawlFeignService;

    @Autowired
    private IBookCategoryService bookCategoryService;

    @Autowired
    private IAuthorService authorService;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private IBookIndexService bookIndexService;

    @Autowired
    private BookInfoService bookInfoService;

    @Autowired
    private IBookContentService bookContentService;

    @Autowired
    private IBookService bookService;

    @Autowired
    private UserClient userClient;


    private ExecutorService executor = Executors.newCachedThreadPool();

//    private BookIndexMapper bookIndexMapper;




    @Override
    public Page<BookVo> listBlank(SearchParam param) {
        //从redis中查询相关信息
        //根据type判断操作类型
        QueryWrapper<Book> bookQueryWrapper = new QueryWrapper<>();
        /**
         * workDirection 根据情况是否有该参数
         * queryType 代表着通过什么方式排序
         * current  当前页面
         * size     每一页查询的条数
         */


        //配置浏览量查询
        bookQueryWrapper.orderByDesc("visit_count");
        PageParam pageParams = PageUtils.getPageParams();

        Page<Book> bookPage = new Page<>(pageParams.getCurrent(), pageParams.getSize());
        this.page(bookPage, bookQueryWrapper);

        Page<Book> page = null;
        //根据最后更新的时间查询
        if (param.getWorkDirection()!=null) {
            //根据性别和浏览量查询
            page = listWorkDirection(bookQueryWrapper, param, pageParams);
        } else if (param.getQueryType() == 0) {
            //根据浏览量查询
            page = this.listClickBlank(bookQueryWrapper, pageParams);
        } else if (param.getQueryType() == 1) {
            //根据更新时间查询
            page = listUpdateBlank(bookQueryWrapper, pageParams);
        }
        List<Book> books = page.getRecords();

        //将book转换成bookVo
        List<BookVo> collect = books.stream().map(book -> {
            BookVo bookVo = new BookVo();
            BeanUtils.copyProperties(book, bookVo);
            return bookVo;
        }).collect(Collectors.toList());

        //想首页的信息保存到redis中
        String json = JSON.toJSONString(collect);
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(RedisConstant.HOME_BOOK_KEY + param.getQueryType(), json, 1, TimeUnit.HOURS);
        Page<BookVo> bookVoPage = new Page<>();
        BeanUtils.copyProperties(page, bookVoPage);
        bookVoPage.setRecords(collect);
        return bookVoPage;

    }

    private Page<Book> listWorkDirection(QueryWrapper<Book> bookQueryWrapper, SearchParam param, PageParam pageParam) {
        bookQueryWrapper.eq("work_direction", param.getWorkDirection());
        bookQueryWrapper.orderByDesc("visit_count");

        Page<Book> page = this.page(new Page<>(pageParam.getCurrent(), pageParam.getSize()), bookQueryWrapper);

        return page;
    }

    /**
     * 增加点击量
     */
    @Override
    public void addVisitCount(Long bookId) {
        bookMapper.addVisitCount(bookId);
    }

   /* @Override
    public List<BookVo> listBookByCondition(SearchParam searchParam) throws IOException {
        QueryWrapper<Book> bookQueryWrapper = new QueryWrapper<>();

        if (StringUtils.isEmpty(searchParam.getQueryWord())) {
            List<BookVo> bookVos = listAllBook();
            return bookVos;
        }
        //添加条件
        Result<List<BookTo>> listResult = searchFeignService.searchBook(searchParam);
        if (listResult.getCode() != 200) {
            throw new NovelGlobalException(listResult.getCode(), listResult.getMsg());
        }
        if (CollectionUtils.isEmpty(listResult.getData())) {
            //异步调用，爬取页面
            CompletableFuture.runAsync(()->{
                try {
                    crawlFeignService.crawlBook(searchParam);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            },threadPoolExecutor);

            return null;
        }

        //将从elasticsearch中查询到的数据重新封装
        List<BookVo> bookVos = bookBySearchCondition(listResult);

        return bookVos;
    }*/

    /**
     * 根据排行榜分类查询小说信息
     *
     * @return
     */
    @Override
    public BookAndRankVo listBookByRank(Long typeId) {
        QueryWrapper<Book> bookQueryWrapper = new QueryWrapper<>();
        bookQueryWrapper.eq("rank_type_id", typeId);
        bookQueryWrapper.orderByDesc("visit_count");
        List<Book> books = this.baseMapper.selectList(bookQueryWrapper);
        if (CollectionUtils.isEmpty(books)) {
            throw new NovelGlobalException(ResultEnum.NOT_DATA.getCode(), ResultEnum.NOT_DATA.getMsg());
        }
        List<BookVo> bookVos = books.stream().map(book -> {
            BookVo bookVo = new BookVo();
            BeanUtils.copyProperties(book, bookVo);
            return bookVo;
        }).collect(Collectors.toList());
        //  将排行分类以及排行分类的父类查出，并封装起来

        BookAndRankVo bookAndRankVo = getRankInfoByTypeId(typeId);
        bookAndRankVo.setBookVos(bookVos);

        //将相关信息添加到redis中
        ZSetOperations<String, String> stringStringZSetOperations = redisTemplate.opsForZSet();

        return bookAndRankVo;
    }

    @Override
    public List<BookVo> listAllBook() {
        //查询所有小说数据
        List<Book> books = this.baseMapper.selectList(null);
        List<BookVo> bookVos = books.stream().map(book -> {
            BookVo bookVo = new BookVo();
            BeanUtils.copyProperties(book, bookVo);
            return bookVo;
        }).collect(Collectors.toList());
        return bookVos;
    }

    @Override
    public BookVo bookInfoById(String bookId) {
        Book book = this.getById(bookId);
        BookVo bookVo = new BookVo();
        BeanUtils.copyProperties(book, bookVo);

        return bookVo;
    }

    @Override
    public List<BookTo> bookListByBatchId(List<Long> batchId) {
        //批量查询小说信息
        List<Book> books = this.baseMapper.selectBatchIds(batchId);
        List<BookTo> bookTos = books.stream().map(book -> {
            BookTo bookTo = new BookTo();
            BeanUtils.copyProperties(book, bookTo);
            return bookTo;
        }).collect(Collectors.toList());
        return bookTos;
    }

    @Override
    public void updateLastIndex(List<BookChapterTo> bookChapterTos) {
        log.info("开始修改小说最新章节信息");
        long startTime = new Date().getTime();
        BookChapterTo bookChapterTo = bookChapterTos.get(bookChapterTos.size() - 1);
        Book book = new Book();
        book.setLastIndexId(bookChapterTo.getIndexId());
        book.setLastIndexName(bookChapterTo.getChapterName());
        book.setUpdateTime(LocalDateTime.now());
        book.setId(bookChapterTo.getBookId());
        this.updateById(book);
        log.info("修改小说最新章节信息完成，耗时：" + (new Date().getTime() - startTime) / 1000.0 + "秒");
    }

    @Transactional
    @Override
    public Book saveBookInfo(Book book) throws ExecutionException, InterruptedException {
        //根据小说名和爬虫源判断小说是否存在
        Book book1 = getByValue(book);
        if (book1 != null) {
            return book1;
        }
        //判断分类是否存在，如果存在，不执行操作，如果不存在，插入分类
        CompletableFuture<BookCategory> bookCategoryCompletableFuture = CompletableFuture.supplyAsync(() -> {
            BookCategory bookCategory = new BookCategory();
            bookCategory.setName(book.getCatName());
            bookCategoryService.insertCategory(bookCategory);
            return bookCategory;
        }, threadPoolExecutor);
        // 判断作者是否存在，如果存在，不执行操作，如果不存在，插入作者信息
        CompletableFuture<Author> authorCompletableFuture = CompletableFuture.supplyAsync(() -> {
            Author author = new Author();
            author.setPenName(book.getAuthorName());
            authorService.insertAuthor(author);
            return author;
        }, threadPoolExecutor);
        CompletableFuture.allOf(bookCategoryCompletableFuture, authorCompletableFuture).get();

        book.setAuthorId(authorCompletableFuture.get().getId());
        book.setCatId(bookCategoryCompletableFuture.get().getId());
        book.setId(IdWorker.getIdStr());
        book.setUpdateTime(LocalDateTime.now());
        //保存bookTo信息
        save(book);
        return book;
    }

    @Override
    public Page<Book> listBook(Book book) {
        QueryWrapper<Book> bookQueryWrapper = new QueryWrapper<>();
        if (book.getBookStatus() != null) {
            bookQueryWrapper.eq("book_status", book.getBookStatus());
        }
        if (!StringUtils.isEmpty(book.getBookName())) {
            bookQueryWrapper.like("book_name", book.getBookName());
        }
        if (!StringUtils.isEmpty(book.getAuthorName())) {
            bookQueryWrapper.like("author_name", book.getAuthorName());
        }
        BaseUtils.compareTime(bookQueryWrapper, book);
        PageParam pageParam = PageUtils.getPageParams();
        Page<Book> page = this.page(new Page<Book>(pageParam.getCurrent(), pageParam.getSize()), bookQueryWrapper);
        return page;
    }


    @Override
    public Page<Book> uncategoryBook(Long categoryId) {
        QueryWrapper<Book> bookQueryWrapper = new QueryWrapper<>();
        bookQueryWrapper.eq("cat_id", categoryId);
        PageParam pageParams = PageUtils.getPageParams();
        Page<Book> page = this.page(new Page<Book>(pageParams.getCurrent(), pageParams.getSize()), bookQueryWrapper);
        return page;
    }

    @Override
    public void addBook(String bookId, String categoryId) {
        bookMapper.addBook(bookId, categoryId);
    }

    @Override
    public Page<BookCategory> unaddCategory(String bookId) {
       Page<BookCategory> bookCategoryPage = bookMapper.unaddCategory(PageUtils.getPage(),bookId);
       return bookCategoryPage;
    }

    @Override
    public List<Book> isAdd(Book book) {
        QueryWrapper<Book> bookQueryWrapper = new QueryWrapper<>();
        String keyword = book.getKeyword();
        bookQueryWrapper.eq("crawl_source_id", book.getCrawlSourceId());
        bookQueryWrapper.and(
                queryWrapper -> queryWrapper.like("book_name", keyword)
                        .or().like("author_name", keyword)
                        .or().like("book_desc", keyword)
        );
        List<Book> books = this.baseMapper.selectList(bookQueryWrapper);
        return books;
    }

    @OperLogAnno(module = ModuleEnum.BOOK, operator = OperEnum.DELETE, desc = "根据id批量删除小说")
    @Override
    public void deleteById(String[] ids) {
        List<String> idList = Arrays.asList(ids);
        this.baseMapper.deleteBatchIds(idList);
        //投递消息到消息队列中，删除该小说的所有章
        rabbitTemplate.convertAndSend(RabbitConstant.DELETE_BOOK_EXCHANGE,RabbitConstant.DELETE_BOOK_ROUTING_KEY,idList);
  }

    @Override
    public BookIndexVo bookIndex(String bookId) throws ExecutionException, InterruptedException {
        CompletableFuture<Book> bookCompletableFuture = CompletableFuture.supplyAsync(() -> {
            Book book = this.baseMapper.selectById(bookId);
            return book;
        });
        PageParam pageParams = PageUtils.getPageParams();
        CompletableFuture<Page<BookIndex>> bookIndicesCompletableFuture = CompletableFuture.supplyAsync(() -> {
            Page<BookIndex> bookIndices = bookIndexService.indexList(bookId,pageParams);
            return bookIndices;
        });
        BookIndexVo bookIndexVo = new BookIndexVo();
        bookIndexVo.setId(bookId);
        bookIndexVo.setBookIndices(bookIndicesCompletableFuture.get());
        BeanUtils.copyProperties(bookCompletableFuture.get(),bookIndexVo);
        return bookIndexVo;
    }

    @Override
    public void updateBook(Book book) {
        book.setUpdateTime(LocalDateTime.now());
        //查询分类表，得到分类表的相关信息
        Integer catId = book.getCatId();
        BookCategory category = bookCategoryService.getById(catId);
        book.setCatName(category.getName());
        baseMapper.updateById(book);
    }

    @Override
    public void disableBookByCategory(Integer categoryId) {
        UpdateWrapper<Book> bookUpdateWrapper = new UpdateWrapper<>();
        bookUpdateWrapper.eq("cat_id",categoryId).set("status",false);
        update(bookUpdateWrapper);
    }

    @Override
    public void changeBookStatus(String id, Boolean status) {
        UpdateWrapper<Book> bookUpdateWrapper = new UpdateWrapper<>();
        bookUpdateWrapper.eq("id",id).set("status",status);
        update(bookUpdateWrapper);
    }

    /**
     *  小说新书榜查询
     *  cacheManager ：缓存管理器，用来管理一类缓存，可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。
     *  cacheNames/value ：用来指定缓存组件的名字
     *  gt  就是 greater than大于  bookNewestRankCache
     * @return
     */
    @Override
    public Result<List<BookRankRespDto>> listNewestRankBooks() {
        QueryWrapper<BookInfo> bookInfoQueryWrapper = new QueryWrapper<>();
        bookInfoQueryWrapper.select(DatabaseConsts.BookInfo.DISINCT_COLUMNS).gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT,0)
                .orderByDesc(DatabaseConsts.CommonColumnEnum.CREATE_TIME.getName());
        return  Result.success(bookInfoService.listNewestRankBooks(bookInfoQueryWrapper));  // 列表排行榜处理
    }

    /**
     * 小说点击榜查询接口
     */
    @Override
    public Result<List<BookRankRespDto>> listVisitRankBooks() {
        return Result.success(bookInfoService.listVisitRankBooks());
    }

    /**
     * 小说更新榜查询接口
     */
    @Override
    public Result<List<BookRankRespDto>> listUpdateRankBooks() {
        return Result.success(bookInfoService.listUpdateRankBooks());
    }

    @Override
    public Result<List<BookInfoRespDto>> listRecBooks(Long bookId) {
        return null;
    }

    /**
     *
     * @param bookId
     * @return
     */
    @Override
    public Result<BookInfoRespDto> getBookById(String bookId,String token) {
        return Result.success(cachePutBookInfo(bookId,token));
    }

    /**
     * 小说最新章节相关信息查询接口
     */
    @Override
    public Result<BookChapterAboutRespDto> getLastChapterAbout(String bookId,String token) {
        // 1、查询小说信息
        BookInfoRespDto bookInfoRespDto = cachePutBookInfo(bookId,token);
        // 2、查询最新章节信息(根据章节的id，查询章节表，book_index表的处理)
        BookChapterRespDto bookChapterRespDto = bookIndexService.getChapter(bookInfoRespDto.getLastChapterId());
        // 3、查询章节内容(根据最新章节id，查询出章节内容,查询content表)
        String content = bookContentService.getBookContent(bookInfoRespDto.getLastChapterId());
        // 4、查询章节的总数
        QueryWrapper<BookIndex> indexQueryWrapper = new QueryWrapper<>();
        indexQueryWrapper.eq("book_id", bookId);
        Long chapterTotal = bookIndexService.selectCount(indexQueryWrapper);
        return Result.success(BookChapterAboutRespDto.builder()
                .chapterInfo(bookChapterRespDto)
                .chapterTotal(chapterTotal)
                .contentSummary(content.substring(0, 30))
                .build());
    }

    /**
     *
     * @param chapterId 章节id
     * @return 上一节的id,String类型的
     */
    @Override
    public Result<String> getPreChapterId(String chapterId, String token) {
        // 1、查询小说ID 和 章节号
        BookChapterRespDto chapter = bookIndexService.getChapter(chapterId);
        String bookId = chapter.getBookId();
        Integer chapterNum = chapter.getChapterNum();

        // 返回给用户模块，用来记录用户的阅读记录
        if (!StringUtils.isEmpty(token)) {
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    userClient.saveUserBookShelf(bookId, chapterId, token);
                }
            });
        }
        // 2、查询上一节的id，并且返回章节ＩＤ, id = bookId and < 章节号 and 降序 ，取一个，就是最上面的
        QueryWrapper<BookIndex> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID,bookId)
                .lt("index_num",chapterNum)
                .orderByDesc("index_num")
                .last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
        // 3、获取到前一章的信息了
        BookIndex bookIndex = bookIndexService.selectOne(queryWrapper);
        // 4、返回对应章节的id就行了
        // 这是java8的新特性，就是bookChapter不为空，返回getId,否则返回null
        return Result.success(Optional.ofNullable(bookIndex)
                .map(BookIndex::getId)
                .orElse(null));
    }

    // 下一章的处理
    @Override
    public Result<String> getNextChapterId(String chapterId,String token) {
        // 1、查询小说ID 和 章节号
        BookChapterRespDto chapter = bookIndexService.getChapter(chapterId);
        String bookId = chapter.getBookId();
        Integer chapterNum = chapter.getChapterNum();

        // 2、查询下一节的id，并且返回章节ＩＤ, id = bookId and > 章节号 and 降序 ，取一个，就是最上面的
        QueryWrapper<BookIndex> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID,bookId)
                .gt("index_num",chapterNum)
                .orderByAsc("index_num")
                .last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
        // 3、获取到前一章的信息了
        BookIndex bookIndex = bookIndexService.selectOne(queryWrapper);
        // 4、返回对应章节的id就行了
        // 这是java8的新特性，就是bookChapter不为空，返回getId,否则返回null
        return Result.success(Optional.ofNullable(bookIndex)
                .map(BookIndex::getId)
                .orElse(null));
    }

    /**
     * 小说内容相关信息查询接口,根据章节id，来查询章节的一些信息
     */
    @Override
    public Result<BookContentAboutRespDto> getBookContentAbout(String chapterId,String token) {
        // 1、根据章节id，查询章节信息
        BookChapterRespDto bookChapter = bookIndexService.getChapter(chapterId);
        String bookId = bookChapter.getBookId();
        // 2、最新章节id的获取处理

        // 记录一下
        if (!StringUtils.isEmpty(token)){
            String finalChapterId = chapterId;
            executor.submit(new Runnable(){
                @Override
                public void run(){
                    userClient.saveUserBookShelf(bookId, finalChapterId,token);
                }
            });
        }


        // 2、根据章节id，查询章节内容
        String content = bookContentService.getBookContent(chapterId);
        // 3、查询小说信息
        BookInfoRespDto bookInfo = cachePutBookInfo(bookId,token);
        return Result.success(BookContentAboutRespDto.builder()
                .bookInfo(bookInfo)
                .chapterInfo(bookChapter)
                .bookContent(content)
                .build());
    }


//    @Override
//    public RestResp<PageRespDto<BookInfoRespDto>> searchBooks(BookSearchReqDto condition) {
//        Page<BookInfoRespDto> page = new Page<>();
//        page.setCurrent(condition.getPageNum());
//        page.setSize(condition.getPageSize());
//        List<BookInfo> bookInfos = bookIndexMapper.searchBooks(page,condition);
//        return RestResp.ok(
//                PageRespDto.of(condition.getPageNum(), condition.getPageSize(), page.getTotal(),
//                        bookInfos.stream().map(v -> BookInfoRespDto.builder()
//                                .id(v.getId())
//                                .bookName(v.getBookName())
//                                .categoryId(v.getCategoryId())
//                                .categoryName(v.getCategoryName())
//                                .authorId(v.getAuthorId())
//                                .authorName(v.getAuthorName())
//                                .wordCount(v.getWordCount())
//                                .lastChapterName(v.getLastChapterName())
//                                .build()).collect(Collectors.toList())));
//    }

    @Override
    public Result<List<BookCategoryRespDto>> listCategory(Integer workDirection) {
        QueryWrapper<BookCategory> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(DatabaseConsts.BookCategoryTable.COLUMN_WORK_DIRECTION, workDirection);
        List<BookCategory> bookCategoryList =  bookCategoryService.selectList(queryWrapper);
        List<BookCategoryRespDto> collect = bookCategoryList.stream().map(v ->
                BookCategoryRespDto.builder()
                        .id(v.getId())
                        .name(v.getName())
                        .build()).collect(Collectors.toList());
        return Result.success(collect);
    }


    /**
     * 查出来后返回的是BookInfo
     *
     * @param id
     * @return
     */
    @Override
    public BookInfo selectById(String id) {
        Book book = baseMapper.selectById(id);
        BookInfo bookInfo = new BookInfo();
        BeanUtils.copyProperties(book,bookInfo);
        // 存最新章节id
        bookInfo.setLastChapterId(book.getLastIndexId());
        return bookInfo;
    }

    @Override
    public Page<Book> searchBooks(SearchParam searchParam) throws IOException {
        //发送请求，获得搜索结果
        Result<Page<Book>> pageResult = searchFeignService.searchBook(searchParam);
        Page<Book> bookPage = pageResult.getData();
        return bookPage;
    }

    @Override
    public void bookRecommend() {
        List<Book> books = baseMapper.selectList(null);
        List<BookInfo> bookInfos = new ArrayList<>();
        //根据点击量获得排序之后的集合
        List<Book> visitBooks = visitBooks(books);
        //根据评分获取排序之后的集合
        List<Book> scoreBooks =scoreBooks(books);

        //获得点击量和评分总和排序之后的集合
        List<Book> boutiques= boutique(books);
        List<Book> carouselBooks = new ArrayList<>();
        Integer count = books.size()>4?4:books.size();
        for (int index = 0; index < count; index++) {

            Book book = books.get(index);
            carouselBooks.add(book);
        }

        //将这三个小说整合到一起
        bookInfoService.saveBookInfo(visitBooks, 2);
        bookInfoService.saveBookInfo(scoreBooks, 3);
        bookInfoService.saveBookInfo(boutiques, 4);
        //判断是否有有轮播图，如果有轮播图，则不做任何操作，如果没有轮播图，则添加4个轮播图
        List<BookInfo> carouseBookInfos = bookInfoService.selectByType(4);
        if (CollectionUtils.isEmpty(carouseBookInfos)) {
            bookInfoService.saveBookInfo(carouselBooks, 0);
        }
    }


    private List<Book> boutique(List<Book> books) {
        List<Book> boutiques = new ArrayList<>();
        boutiques.addAll(books);
        Collections.sort(boutiques, new Comparator<Book>() {
            @Override
            public int compare(Book book1, Book book2) {
                //分别计算出两个的总评分
                double totalScore1 = (5 - 5 * Math.pow(book1.getVisitCount(), 1 / 5.0)) + book1.getScore() / 2.0;
                double totalScore2 = (5 - 5 * Math.pow(book2.getVisitCount(), 1 / 5.0)) + book2.getScore() / 2.0;
                //TODO
                if (totalScore1 > totalScore2) {
                    return 1;
                }
                if (totalScore1 == totalScore2) {
                    return 0;
                }
                return -1;
            }
        });
        Collections.reverse(boutiques);
        return boutiques;
    }

    private List<Book> scoreBooks(List<Book> books) {
        List<Book> scoreBooks = new ArrayList<>();
        scoreBooks.addAll(books);
        Collections.sort(scoreBooks, new Comparator<Book>() {
            @Override
            public int compare(Book book1, Book book2) {
                if (book1.getScore() > book2.getScore()) {
                    return 1;
                }
                if (book1.getVisitCount().equals(book2.getVisitCount())) {
                    return 0;
                }
                return -1;
            }
        });
        Collections.reverse(scoreBooks);
        return scoreBooks;

    }

    private List<Book> visitBooks(List<Book> books) {
        List<Book> visitBooks = new ArrayList<>();
        visitBooks.addAll(books);
        Collections.sort(visitBooks, new Comparator<Book>() {
            @Override
            public int compare(Book book1, Book book2) {
                if (book1.getVisitCount() > book2.getVisitCount()) {
                    return 1;
                }
                if (book1.getVisitCount().equals(book2.getVisitCount())) {
                    return 0;
                }
                return -1;
            }
        });
        Collections.reverse(visitBooks);
        return visitBooks;

    }

    @Transactional
    @Override
    public void saveBook(List<DataPart> dataParts) {
        //查询数据库中是否该小说
        if (CollectionUtils.isEmpty(dataParts)) {
            return;
        }
        DataPart dataPart = dataParts.get(0);
        //判断是否有分类
        QueryWrapper<Book> bookQueryWrapper = new QueryWrapper<>();
        bookQueryWrapper.eq("book_name", dataPart.getBookName());
        Book book = this.baseMapper.selectOne(bookQueryWrapper);
        BookCategory bookCategory = bookCategoryService.saveCategory(dataPart);
        //插入章节信息
        List<BookIndex> bookIndices = bookIndexService.saveIndex(dataParts, book);
        if (CollectionUtils.isEmpty(bookIndices)){
            return;
        }
        Map<String, BookIndex> bookIndexMap = new HashMap<>();
        for (BookIndex bookIndex : bookIndices) {
            bookIndexMap.put(bookIndex.getIndexName(), bookIndex);
        }

        //插入章节内容
        saveContent(dataParts, bookIndexMap);
        //得到最后一张的章节信息

        BookIndex bookIndex = bookIndices.get(bookIndices.size() - 1);

        Integer wordCount = (book == null || book.getWordCount() == null) ? 0 : book.getWordCount();
        for (BookIndex index : bookIndices) {
            wordCount = wordCount + index.getWordCount();
        }
        if (!ObjectUtils.isEmpty(book)) {
            book.setLastIndexId(bookIndex.getId());
            book.setLastIndexName(bookIndex.getIndexName());
            book.setLastIndexUpdateTime(LocalDateTime.now());
            book.setUpdateTime(LocalDateTime.now());
            book.setWordCount(wordCount);
            this.updateById(book);
            return;
        }
        book = new Book();
        BeanUtils.copyProperties(dataPart, book);
        String url = null;
        try {
            url = OssUtils.uploadNetworkFlow(dataPart.getPicUrl());
        } catch (Exception e) {
            e.printStackTrace();
            url = dataPart.getPicUrl();
        }
        book.setPicUrl(url);
        book.setId(dataPart.getBookId());
        book.setLastIndexId(bookIndex.getId());
        book.setLastIndexName(bookIndex.getIndexName());
        book.setLastIndexUpdateTime(LocalDateTime.now());
        book.setUpdateTime(LocalDateTime.now());
        book.setCreateTime(LocalDateTime.now());
        book.setCatId(bookCategory.getId());
        book.setWordCount(wordCount);
        this.save(book);

    }

    private void saveContent(List<DataPart> dataParts, Map<String, BookIndex> bookIndexMap) {
        List<BookContent> collect = dataParts.stream().filter(dataPart -> {
            BookIndex bookIndex = bookIndexMap.get(dataPart.getIndexName());
            if (bookIndex == null || bookIndex.getId() == null) {
                return false;
            }
            return true;
        }).map(dataPart -> {
            BookContent bookContent = new BookContent();
            BookIndex bookIndex = bookIndexMap.get(dataPart.getIndexName());
            bookContent.setId(IdWorker.getIdStr());
            bookContent.setIndexId(bookIndex.getId());
            bookContent.setContent(dataPart.getContent());
            return bookContent;
        }).collect(Collectors.toList());
        bookContentService.saveBatch(collect);

    }


    private Book getByValue(Book book) {
        QueryWrapper<Book> bookQueryWrapper = new QueryWrapper<>();
        bookQueryWrapper.eq("book_name", book.getBookName())
                .eq("crawl_source_id", book.getCrawlSourceId())
                .eq("crawl_book_id", book.getCrawlBookId());
        return this.baseMapper.selectOne(bookQueryWrapper);
    }

    private List<BookVo> bookBySearchCondition(Result<List<BookTo>> listResult) throws IOException {
        //判断searchCondition 是否为空
        //如果不为空，调用search模块，从elasticsearch中查询数据
        if (listResult.getCode() != 200) {
            return null;
        }
        // 将查询到的数据的id封装成集合
        List<BookTo> bookTos = listResult.getData();
        if (bookTos == null) {
            throw new NovelGlobalException(ResultEnum.NOT_DATA.getCode(), ResultEnum.NOT_DATA.getMsg());
        }
        List<BookVo> bookVos = bookTos.stream().map(bookSearchTo -> {
            BookVo bookVo = new BookVo();
            BeanUtils.copyProperties(bookSearchTo, bookVo);
            return bookVo;
        }).collect(Collectors.toList());

        return bookVos;
    }

    @NotNull
    private BookAndRankVo getRankInfoByTypeId(Long typeId) {
        List<BookRankVo> bookRankVos = bookRankService.listBookRank();
        BookAndRankVo bookAndRankVo = new BookAndRankVo();
        for (BookRankVo bookRankVo : bookRankVos) {
            if (Objects.equals(bookRankVo.getId(), typeId)) {
                bookAndRankVo.setRankId(typeId);
                bookAndRankVo.setRankParentId(bookRankVo.getRankParentId());
                break;
            }
            List<BookRankItemVo> childRankCats = bookRankVo.getChildRankCats();
            for (BookRankItemVo childRankCat : childRankCats) {
                if (Objects.equals(childRankCat.getId(), typeId)) {
                    bookAndRankVo.setRankId(typeId);
                    bookAndRankVo.setRankParentId(childRankCat.getRankParentId());
                    break;
                }
            }
        }
        return bookAndRankVo;
    }


    private Page<Book> listUpdateBlank(QueryWrapper<Book> bookQueryWrapper, PageParam param) {
        bookQueryWrapper.orderByDesc("update_time");
        Page<Book> bookPage = new Page<>(param.getCurrent(), param.getSize());
        Page<Book> page = this.page(bookPage, bookQueryWrapper);

        return page;

    }

    private List<Book> listNewBlank(QueryWrapper<Book> bookQueryWrapper) {
        bookQueryWrapper.orderByDesc("create_time");
        List<Book> books = this.baseMapper.selectList(bookQueryWrapper);
        return books;
    }

    private Page<Book> listClickBlank(QueryWrapper<Book> queryWrapper, PageParam param) {
        queryWrapper.orderByDesc("visit_count");
        Page<Book> bookPage = new Page<>(param.getCurrent(), param.getSize());
        Page<Book> page = this.page(bookPage, queryWrapper);
        return page;
    }

    /**
     * 小说基本信息查询，放在redis中进行处理
     * @param id
     * @return
     */
    public BookInfoRespDto cachePutBookInfo(String id,String token) {
        // redis中不存在 1、基本信息查询处理
        BookInfo bookInfo  = bookService.selectById(id);
        // 2、查询首章节id
        QueryWrapper<BookIndex> queryWrapper = new QueryWrapper<>();
        // ASC升序的处理，然后取的是第一章
        queryWrapper
                .eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID, id)
                .orderByAsc("index_num")
                .last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
        BookIndex firstBookIndex = bookIndexService.selectFirstBook(queryWrapper);
        String id1 = firstBookIndex.getId();
        // 获取到信息（获取firstID用的）
        // 判断token是否过期，过期的话就返回就不处理了，数据还是第一章
        if (!StringUtils.isEmpty(userClient.getUserIds(token))){
            id1 = firstBookIndex.getId();
        }
        // 跟新首章id
        if (!StringUtils.isEmpty(token)){
            // 返回的就是章节id
            id1 = userClient.getProContentId(id, token);
        }



        BookInfoRespDto build = BookInfoRespDto.builder()
                .id(bookInfo.getId())
                .bookName(bookInfo.getBookName())
                .bookDesc(bookInfo.getBookDesc())
                .bookStatus(bookInfo.getBookStatus())
                // 作家id，作家姓名
                .authorId(bookInfo.getAuthorId())
                .authorName(bookInfo.getAuthorName())
                .categoryId(bookInfo.getCategoryId())
                .categoryName(bookInfo.getCategoryName())
                .commentCount(bookInfo.getCommentCount())
                // 改成了String
                .firstChapterId(id1)
                .lastChapterId(bookInfo.getLastChapterId())
                .picUrl(bookInfo.getPicUrl())
                .visitCount(bookInfo.getVisitCount())
                .wordCount(bookInfo.getWordCount())
                .build();
        return build;
    }
}
